	Concentre.objectsHashTable['{http://www.w3.org/2002/xforms}model'] =
	Concentre.XForms_Element_Head_Model = Concentre.Class(Concentre.XForms_Element_Head_Abstract,{

		innerBindings : [],	
		innerInstances: [],
		innerSubmissions: [],
		
		instances: [],
		defaultInstance: null,
		
		initialize: function () {
			this.superclass.initialize.apply(this,arguments);
			this.model = this;

			this.parent.innerModels.unshift(this);
			
			this.parseChildren();

			this.registerEvents('mousewheel','xforms-ready','xforms-rebuild','xforms-recalculate','xforms-revalidate','xforms-refresh');
			this.fireEvent('xforms-ready');

		},	
		

		rebuild: function() {

			var g, c, i, e;
			
			g = this.graph = new DependencyGraph(this);

			this.doRebuild = 0;

			console.log('-- Rebuild --');		
			
			c = this.defaultInstance.instanceNode.documentElement;
			
			for (i=this.innerBindings.length; --i>=0  ;) {
				 this.innerBindings[i].build(c);
			}
				
			for (var i in g.vertices) {
    			var vertex = g.vertices[i];
					
    			if (vertex.property == "relevant" || vertex.property == "readonly") {
    				console.log('add inherited ' + vertex.node.nodeName);
     				addInheritedVertex(vertex, vertex.node);
    			}
  			}
	
			this.subGraph = g.getPertinentSubGraph();
	    
	    	console.log('--- Dependency Graph ---\n'+this.graph);

    		function addInheritedVertex(vertex, descendantNode) {
    			// The first time this function is called, do not add a vertex.
   			 	if (descendantNode != vertex.node) {
     				// If the descendant node already has this property, stop.
      				if (vertex.graph.getVertex(descendantNode, vertex.property) != null) {
        				return;
     				 }

      				// Add a vertex to the descendant node for the inherited property, with a null
      				// XPath.
      
      				var v = vertex.graph.addVertex(descendantNode, vertex.property, null, null);
      				var v2 = vertex.graph.getVertex(descendantNode, 'text', null, null);
      
      				if(v2) { 
      					v.dependsOn(v2);
      				}
      				
      				v.dependsOn(vertex);
          
      				var v3 = vertex.graph.getVertex(descendantNode, 'text');
     				if (v3) {
     					v.controls = v3.controls;
          			}
          		}
    		
				var children   = descendantNode.childNodes;
    	   		var attributes = descendantNode.nodeType==1?descendantNode.attributes:[];

    	   		for (var i = 0; i < children  .length; ++i) { if (children[i].nodeType!=1) continue; addInheritedVertex(vertex, children  [i]); }
    	   		for (var i = 0; i < attributes.length; ++i) { addInheritedVertex(vertex, attributes[i]); }
    	   	}
    	   	
			
  		},	

		recalculate: function() {
			console.log('-- Recalculate --');

			
  var updated = "";
  var subGraph = this.graph.getPertinentSubGraph();

  console.log('\n--- dependency SubGraph ---\n'+ subGraph);
	    	
  var subVertex, independentVertices = [];

  this.doRecalculate = false;
  this.isValid = true;
	
  for (var i = subGraph.vertices.length; --i>=0; ) {
    var vertex = subGraph.vertices[i];  
   	if (!vertex) continue;
   	if (vertex.inDegree == 0) {
      independentVertices.unshift(vertex);
    }
  }		                                                    	

 
  while (subVertex = independentVertices.shift()) {
	  var vertex    = this.graph.vertices[subVertex.index];
 		    
	    for (var i = subVertex.dependents.length; --i>=0; ) {
	      var dependent = subVertex.dependents[i];
	      
	      if (--dependent.inDegree == 0) {
	        independentVertices.push(dependent);
	  	  }
	  	}
 		
  	if (updated != "") {
      updated += ", ";
    }

    updated += vertex;
  	  switch (vertex.property) {
      case "text":
        updated += " = \"" + vertex.getValue() + "\"";
        vertex.touch();
       	break;
      case "calculate":
     	var value = XPath.string(vertex.xpath(vertex.context,QName));
   	
   		updated += " := \"" + value + '|' + vertex.context.contextPosition +"\"";
	    this.graph.getVertex(vertex.node, "text").setValue(value);
       	break;
        
      case "relevant":
      case "readonly":
		
        switch (vertex.property) {
	        case "relevant": var defaultValue = true;  break;
	        case "readonly": var defaultValue = false; break;
        }
      
        //var parentNode   = concentre.factory.domutils.getOwnerElement(vertex.node);
		var parentNode = vertex.node.parentNode;
        var parentVertex = (parentNode == null) ? null : this.graph.getVertex(parentNode, vertex.property);

        if (parentVertex != null && parentVertex.value && parentVertex.value!=defaultValue) {
	 		var value = parentVertex.getValue();
	  		//alert(vertex + '(hasParent)' + parentVertex + value);
        }
        else if (vertex.xpath != null) {
          var value = XPath.bool(vertex.xpath(vertex.context,QName));
	  	  //alert(vertex + '=' + value);
	  
        }
        else {
          var value = defaultValue;
	  //alert('(default)' + vertex + '=' + value);
        }
        
        updated += " := " + value;
		vertex.setValue(value);
        
        break;
           
      case "required":
      case "constraint":
        var value = XPath.bool(vertex.xpath(vertex.context,QName)); 
		this.isValid = this.isValid && value;

        updated += " := " + value;

        vertex.setValue(value);
        break;
			case "type":
			  var datatype = concentre.modules.xmlschemas.datatypesHashTable[ vertex.xpath ]; 
			  if (!datatype) throw new Exception(concentre.factory.i18n.translate('IDS_UNKNOWN_XML_SCHEMA_DATATYPE',vertex.xpath));

				var r = datatype.match ( this.graph.getVertex(vertex.node, "text").getValue() );
				this.isValid = this.isValid && (r === undefined);
				
			  vertex.setValue( r );
				break;
				
      default:
        //throw new XmlException
        alert("Unknown property: " + vertex.property); 
    
	  } 
	}			
	
	console.log(updated);
		},

		revalidate: function() {
			console.log('-- Revalidate --');
			
  
	var refreshed = "";
	this.doRevalidate = false;
	var refreshControls = [];

	
	for (var i=this.graph.changedVertices.length; --i>=0; ) {
	   
	   var vertex = this.graph.changedVertices[i];
	   vertex.refresh();

		
	    for (var j=vertex.controls.length; --j>=0; ) {
	      var c = vertex.controls[j];
	      if (refreshed != "") {
	        refreshed += ", ";
	      }
	      c.touch();
	      refreshed += c;
        }
        
       }

	
	/*
	for (var i=this.changedControls.length; --i>=0; ) {
	     this.changedControls[i].refresh();
	}

	this.changedControls = [];
	*/

		console.log('Refreshed\n'+refreshed);
	
		},

		hasChanged: function() {
			return this.graph.changedVertices.length > 0;
		},

		refresh: function () {
			console.log('-- Refresh --');
			
			this.doRefresh = false;
		 	this.graph.resetChangedVertices();
		},


		handleEvent: function(evt) {
	
			switch (evt.type) {
			case 'xforms-rebuild':
				//this.reloadInstances();
				this.rebuild(); break;
			case 'xforms-recalculate':
				this.recalculate(); break;
			case 'xforms-revalidate':
				this.revalidate(); break;
			case 'xforms-refresh':
				this.refresh(); break;
			}
	
		},

		toString: function() {
			return '[ Object Concentré XForms Model ]';
		}
	});
